<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <div>打开控制台查看结果</div>

    <script>
        /* Iterator的作用有三个：
        1、为各种数据结构，提供一个统一的、简单的访问接口
        2、使得数据结构的成员能够按某种次序排列
        3、ES6创造了一种新的遍历命令for...of，Iterator主要供for...of消费 */

       /* Iterator的遍历过程
          1、创建一个指针对象，指向当前数据结构的其实位置。也就是说，比遍历器对象本质上，就是一个指针对象。
          2、第一次调用指针对象的next方法，可以将指针指向数据结构的第一个成员。
          3、第二次调用指针对象的next方法，指针就指向数据结构的第二个成员。。
          4、不断调用指针对象的next方法，直到它指向数据结构的结束位置。
        每一次调用next方法，都会返回数据结构的当前成员信息。具体来说，就是返回一个包含value和done两个属性的对象。其中value属性是当前成员的值，done属性是一个布尔值，表示遍历是否结束。
       */
      let it = makeIterator(['a','b'])
      console.log(it.next());  
      console.log(it.next());
      console.log(it.next());

      function makeIterator(array){
        let nextIndex = 0;  // index
        return {
            next : function(){
                // index < ['a','b'].length
                // return { value : 'a' , done : false }
                // 如果done返回true则表示未找到，遍历结束
                return nextIndex < array.length ? 
                {value : array[nextIndex++] , done : false} : 
                {value : undefined, done : true}
            }
        }
      }
      /* 上述代码定义了一个markIterator函数，该函数内有一个nextIndex指针，调用指针对象的next方法来移动指针。刚开始指针的初始值是0，每调用一次，指针+1指向下一个，循环遍历，第一次指针为0，第二次指针为1。
      通过done的状态来确定是否遍历结束。
      调用指针对象的next方法，就可以遍历事先给定的数据结构。
      */

      /* 上面的对象是一个完整的Iterator函数，如果改成简写行驶的话，done和value：undefined都是可以省略的，也就是下面的这种简写形式 */

      function makeIterator2(array){
        let nextIndex = 0
        return {
            next: function(){
                return nextIndex < array.length ? 
                {value : array[nextIndex++]} : 
                {done : true}
            }
        }
      }
      let it2 = makeIterator2(['东方','不败'])
      console.log(it2.next());  // 东方
      console.log(it2.next());  // 不败

      // 由于Iterator只是把接口规格（数据）加到数据结构之上（键），所以，遍历器与它所遍历的那个数据结构，实际上是分开的，完全可以写出没有对应数据结构（也就是数据类型，没有对应说的是any类型）的遍历器对象，或者说用遍历器对象模拟出数据结构。下面是一个无线运行的遍历器对象的例子。

      let it3 = idMarker()
      console.log(it3.next());  // {value: 0, done: false}
      console.log(it3.next()); // {value: 1, done: false}
      console.log(it3.next()); // {value: 2, done: false}

      function idMarker() {
        let index = 0;
        return {
            next : function(){
                return { value : index++,done : false}
            }
        }
      }


      console.log('============默认Iterator接口============');
      // Iterator接口的目的，就是为所有数据结构，提供一种统一的访问机制，即for...of循环。当使用for...of循环遍历某种数据结构时，该循环会自动去寻找Iterztor接口。

      /* 一种数据结构只要部署了Iterator接口，那么这种数据结构就是"可遍历的(iterable)" */

      /* ES6规定，默认的Iterator接口部署在数据结构的Symbol.iterator属性上，或者说，一个数据结构只要具有Symbol。iterator属性，就可以认为它是"可遍历的" */

      /* Symbol.iterator属性本身是一个函数，就是当前数据结构默认的遍历器生成函数。执行这个函数，就会返回一个遍历器。至于属性名Symbol.iterator，它是一个表达式，返回Symbol对象的iterator属性，这是一个预定好的、类型为Symbol的特殊值，所以要放在方括号内 */
      let obj = {
        [Symbol.iterator] : function(){
            return {
                next : function(){
                    return {
                        value : 1,
                        done : true
                    }
                }
            }
        }
      }
      console.log(obj);

      // 上面代码中，对象obj是可遍历的（iterable)，因为具有Synbol.iterator属性。执行这个属性，会返回一个遍历器对象。该对象的根本特征就是具有next方法。每次调用next方法，都会返回一个代表当前成员的信息对象，具有value和done两个属性。

      /* 
         ES6的有些数据结构原生具备Iterator接口（比如数组），即不用任何处理，就可以被for...of新欢遍历。原因在于，这些数据结构原生部署了Symbol。iterator尚需经，另外一些数据结构没有（比如对象）。凡是部署了Symbol.iterator属性的数据结构，就成为部署了遍历器接口。调用这个接口，就会返回一个遍历器对象。
      */

      /* 原生具备Iterator接口的数据结构如下：
         Array
         Map
         Set
         String
         TypedArray
         函数的arguments对象
         NodeList对象
      */

      // 下面的例子是数组的Symbol.iterator属性
      let arr = ['a','b','c']
      let iter = arr[Symbol.iterator]()
      console.log(iter.next()); // {value: 'a', done: false}
      console.log(iter.next()); // {value: 'b', done: false}
      console.log(iter.next()); // {value: 'c', done: false}
      console.log(iter.next()); // {value: undefined, done: true}





      


   </script>
</body>
</html>